<?php

namespace Commands;

use App\Model\Server\MasterEnum;
use Database\Connection;
use Database\DatabasesProviders;
use Database\Db;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class GiiRequestFilterCommand extends Command
{


    protected array $hashMaps = [];

    public array $type = [
        'int'    => ['tinyint', 'smallint', 'mediumint', 'int', 'bigint', 'timestamp'],
        'string' => ['char', 'varchar', 'tinytext', 'text', 'mediumtext', 'year', 'longtext', 'date', 'time', 'datetime'],
        'array'  => ['json'],
        'float'  => ['float', 'double', 'decimal',],
        'bool'   => ['boolean',],
    ];


    /**
     * @return void
     */
    protected function configure(): void
    {
        $this->setName('sw:form')
             ->addOption('database', null, InputOption::VALUE_REQUIRED, 'is run daemonize')
             ->addOption('table', null, InputOption::VALUE_OPTIONAL, 'is run daemonize')
             ->addOption('all-table', null, InputOption::VALUE_NONE, 'is run daemonize');

    }


    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $daemon = (int)$input->getOption('all-table');

        $database = \Kiri::getDi()->get(DatabasesProviders::class)->get($input->getOption('database'));

        $namespace = $input->getOption('database');
        if ($daemon) {
            $tables = Db::connect($database)->fetchAll('show tables');
            foreach ($tables as $table) {
                $this->putFile(Db::desc(current($table), $database), $namespace, $database, current($table));
            }
        } else {
            $this->putFile(Db::desc($input->getOption('table'), $database), $namespace, $database, $input->getOption('table'));
        }
        return 0;
    }


    /**
     * @param array $data
     * @param string $namespace
     * @param Connection $database
     * @param string $table
     * @return void
     */
    protected function putFile(array $data, string $namespace, Connection $database, string $table): void
    {


        $injects = [];
        $toArray = [];

        $fieldMaxLength = $this->maxLength($data);
        foreach ($data as $datum) {
            $rules     = [];
            $fieldType = 'string';
            if ($datum['Null'] == 'NO' && $datum['Extra'] != 'auto_increment') {
                $rules[] = '\'required\'';
            }
            if (str_contains($datum['Type'], ' ')) {
                if (str_contains($datum['Type'], 'unsigned')) {
                    $rules[] = '\'min\' => 0';
                }
                $datum['Type'] = explode(' ', $datum['Type'])[0];
            }
            if ($datum['Extra'] != 'auto_increment') {
                $toArray[] = '
            \'' . $datum['Field'] . '\' ' . str_pad('', $fieldMaxLength - mb_strlen($datum['Field']), ' ') . '=> $this->' . $datum['Field'];
            }
            if (str_starts_with($datum['Type'], 'enum')) {
                $trim    = str_replace('enum(', '', $datum['Type']);
                $trim    = str_replace(')', '', $trim);
                $rules[] = '\'in\' => [' . str_replace(',', ', ', $trim) . ']';
            } else if (str_starts_with($datum['Type'], 'set')) {
                $trim    = str_replace('set(', '', $datum['Type']);
                $trim    = str_replace(')', '', $trim);
                $rules[] = '\'in\' => [' . str_replace(',', ', ', $trim) . ']';
            } else {
                $values = explode('(', $datum['Type']);
                if (isset($values[1])) {
                    $values[1] = rtrim($values[1], ')');
                    if (str_contains($values[1], ',')) {
                        $values[1] = explode(',', $values[1]);
                    }
                }
                if (isset($values[1])) {
                    if (is_array($values[1])) {
                        $rules[] = '\'maxLength\' => ' . $values[1][0] . ', \'round\' => ' . $values[1][1];
                    } else {
                        $rules[] = '\'maxLength\' => ' . $values[1];
                    }
                }
                foreach ($this->type as $key => $type) {
                    if (in_array($values[0], $type)) {
                        $fieldType = $key;
                        break;
                    }
                }
            }
            $html = '
    /**
     * ' . (empty($datum['Comment']) ? '这家伙很懒, 什么都没写' : $datum['Comment']) . '
     */
    #[Binding(
        field: \'' . $datum['Field'] . '\',
        rules: ' . (!empty($rules) ? '[' . implode(', ', $rules) . '],' : '[],');
            if ($datum['Default'] != null) {
                $html .= '
        defaultValue: ' . ($datum['Default'] == '' ? '\'\'' : ($fieldType == 'string' ? '\'' . $datum['Default'] . '\'' : $datum['Default']));
            }
            $html      .= '
    )]
    public ' . ($datum['Null'] == 'YES' ? '?' : '') . $fieldType . ' $' . $datum['Field'] . ($datum['Null'] == 'YES' ? ' = null;' : ';');
            $injects[] = $html;
        }

        $dbName = $this->clearXiaHuaXian($namespace);

        $table = str_replace($database->tablePrefix, '', $table);
        if (str_contains($table, '-')) {
            $table = str_replace('-', '_', $table);
        }
        $class = $this->clearXiaHuaXian($table, 'Form');

        $prefix = $dbName == 'Db' ? '' : '\\' . ucfirst($dbName);


        $html = '<?php

declare(strict_types=1);

namespace App\Form' . $prefix . ';


use Kiri\Router\Validator\Binding;


/**
 * Class  App\Form' . $prefix . '\\' . $class . '
 */
class ' . $class . ' implements \Arrayable, \Stringable, \JsonSerializable 
{

' . implode(PHP_EOL . PHP_EOL, $injects) . '


    /**
     * @return bool|string
     */
    public function jsonSerialize(): bool|string
    {
        return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE);
    }


    /**
     * @return string
     */
    public function __toString(): string
    {
        return $this->jsonSerialize();
    }


    /**
     * @return array
     */
    public function toArray(): array
    {
        return [' . implode(',', $toArray) . '
        ];
    }
}';

        $dir = APP_PATH . 'app/Form';
        if ($namespace != 'db') {
            $dir = APP_PATH . 'app/Form/' . ucfirst($dbName);
        }
        if (!is_dir($dir)) {
            mkdir($dir, 0777, true);
        }
        file_put_contents($dir . '/' . $class . '.php', $html);
    }


    /**
     * @param string $table
     * @param string $append
     * @return string
     */
    public function clearXiaHuaXian(string $table, string $append = ''): string
    {
        $class = explode('_', $table);
        if (!empty($append)) {
            $class[] = $append;
        }
        $class = array_map(function ($data) {
            return ucfirst($data);
        }, $class);

        return implode($class);
    }


    /**
     * @param array $data
     * @return int
     */
    public function maxLength(array $data)
    {
        $length = 0;
        foreach ($data as $datum) {
            $_length = mb_strlen($datum['Field']);
            if ($_length > $length) {
                $length = $_length;
            }
        }
        return $length;
    }


    /**
     * @return bool
     */
    protected function hasDefaultValue(): bool
    {
        return true;
    }


    /**
     * @param \ReflectionClass $object
     * @param $className
     * @param $method
     * @return string
     * @throws \ReflectionException
     */
    public function getFuncLineContent(array $explode, int $start, int $end): string
    {
        $exists = array_slice($explode, $start - 1, $end - $start + 1);
        return implode(PHP_EOL, $exists);
    }


    /**
     * @param string $fileName
     * @param \ReflectionClass $class
     * @return string
     */
    protected function getImports(\ReflectionClass $class): string
    {
        $startLine = 1;
        $array     = [];
        $fileOpen  = fopen($class->getFileName(), 'r');
        while (($content = fgets($fileOpen)) !== false) {
            if (str_starts_with($content, 'use ')) {
                $array[] = $content;
            }
            if ($startLine == $class->getStartLine()) {
                break;
            }
            ++$startLine;
        }
        return implode($array);
    }
}